import { BaseSymbol, FunctionArgument, FunctionSymbol } from "./PL00Symbols";
class PL00SymbolTable {
  table: { [name: string]: BaseSymbol[] } = {};
  scopes: string[][] = [];
  modules: { [name: string]: boolean } = {};
  constructor() {}

  /**
   * 进入作用域
   */
  push() {
    this.scopes.push([]);
  }

  /**
   * 退出作用域
   */
  pop() {
    let names = this.scopes.pop();
    // 按名删除
    for (const name of names) {
      this.table[name].pop();
    }
  }

  /**
   * 添加符号
   * @param name 标识符名
   * @param value 符号
   */
  add(name: string, value: BaseSymbol) {
    if (!(name in this.table)) {
      this.table[name] = [];
    }
    this.scopes[this.scopes.length - 1].push(name);
    // console.log(name)
    this.table[name].push(value);
  }

  /**
   * 获得符号
   * @param name 标识符名
   */
  get(name: string): BaseSymbol {
    if (!(name in this.table)) {
      // console.log(name + " not defined");
      return null;
    }
    const vars = this.table[name];
    const target = vars[vars.length - 1];
    return target;
  }

  /**
   * 获得函数
   * @param name 函数名
   * @param params 参数列表
   */
  getFunction(name: string, params: FunctionArgument[]): FunctionSymbol {
    // 没有同名函数
    if (!(name in this.table)) {
      return null;
    }
    const paramsLength = params.length;
    const vars = this.table[name] as FunctionSymbol[];
    for (const func of vars.reverse()) {
      // 参数长度对不上
      if (func.params.length !== params.length) {
        continue;
      }
      let flag = true;
      for (let i = 0; i < paramsLength; ++i) {
        if (params[i] === null || params[i] === undefined) {
          return null;
        }
        // console.log(func.params[i]);
        // Any类型其实也是一个糟糕的设定（小声）
        //  因为这里的函数重载匹配的优先级完全是按照顺序的
        //  当然PL00不支持隐式类型转换和变长参数什么的一般不会冲突
        //  但是，Any的出现会导致这种冲突，使得优先级真正影响了匹配结果
        if (
          func.params[i].type !== params[i].type &&
          func.params[i].type != "Any"
        ) {
          flag = false;
          break;
        }
      }
      if (flag === true) {
        return func;
      }
    }
    return null;
  }

  /**
   * 重新赋值
   * @param name 标识符名称
   * @param value 值
   */
  bindValue(name: string, value = 0) {
    // 这个方法好像也没什么用
    const target = this.get(name);
    target.value = value;
  }

  /**
   * 引入模块
   * @param name 模块名
   */
  importModule(name: string) {
    // 虽然一开始有计划在符号表中管理模块
    // 但这已经是废设了
    this.modules[name] = true;
  }
}
export { PL00SymbolTable };
